/*!
 * Copyright (c) 2007-2009, stauren.net. All rights reserved.
 * core file of double-underscore library
 * http://code.google.com/p/double-underscore/
 * @file core.js
 * @modules Lang, Log(simple), Dom, Selector, Io, Event, Cookie, Class, Drag
 * @revision:
 * @author <a href="mailto:stauren@stauren.net">stauren</a>
 */

(function(_scope) {

var _NAME = '__';
_scope[_NAME] = _scope[_NAME] || {};

if (_scope[_NAME].duLoaded || !_scope[_NAME].add) {
  return;
} else {
  _scope[_NAME].duLoaded = true;
}

var _SELF = _scope[_NAME],
_WINDOW = _SELF.config.win,
_DOCUMENT = _SELF.config.doc,
_DOCUMENTELEMENT = _DOCUMENT.documentElement,
_CFG = _SELF.cfg,
_UNDEFINED,
_TRUE = true,
_FALSE = false,
_NULL = null,
_LENGTH = 'length',
_STRING = 'string',
_NUMBER = 'number',
_FUNCTION = 'function',
_APPLY = 'apply',
_TOSTRING = 'toString',
_SPLIT = 'split',
_INDEXOF = 'indexOf',
_APPENDCHILD= 'appendChild',
_PROTOTYPE = 'prototype',
_DATAAVAILABLE = 'dataavailable',
_GETELEMENTBYID = 'getElementById',
_GETELEMENTSBYCLASSNAME = 'getElementsByClassName',
_GETELEMENTSBYTAGNAME = 'getElementsByTagName',
_COMPAT_MODE = 'compatMode',
_CSS1_COMPAT = 'CSS1Compat',
_ARRAY_TYPE = '[object Array]',

_OP = Object[_PROTOTYPE],


_GLOBAL_VAR = {},
_EF = function() {},
_K = function(x) {return x;},

_OPER = {
  "+": function(a, b){return a + b;},
  "-": function(a, b){return a - b;},
  "*": function(a, b){return a * b;},
  "/": function(a, b){return a / b;},
  "%": function(a, b){return a % b;},
  "-2": function(a, b){return b - a;},
  "/2": function(a, b){return b / a;},
  "%2": function(a, b){return b % a;},
  ">": function(a, b){return a > b;},
  "<": function(a, b){return a < b;},
  ">=": function(a, b){return a >= b;},
  "<=": function(a, b){return a <= b;},
  "==": function(a, b){return a == b;},
  "===": function(a, b){return a === b;},
  "&&": function(a, b){return a && b;},
  "||": function(a, b){return a || b;},
  "&": function(a, b){return a & b;},
  "|": function(a, b){return a | b;},
  "^": function(a, b){return a ^ b;},
  "++": function(a){return a + 1;},
  "--": function(a){return a - 1;},
  "!": function(a){return !a;}
},

_NODE_TYPE = {
  ELEMENT_NODE: 1,
  ATTRIBUTE_NODE: 2,
  TEXT_NODE: 3,
  CDATA_SECTION_NODE: 4,
  ENTITY_REFERENCE_NODE: 5,
  ENTITY_NODE: 6,
  PROCESSING_INSTRUCTION_NODE: 7,
  COMMENT_NODE: 8,
  DOCUMENT_NODE: 9,
  DOCUMENT_TYPE_NODE: 10,
  DOCUMENT_FRAGMENT_NODE: 11,
  NOTATION_NODE: 12
},

_id = function(s) {
  return typeof s == _STRING ? _DOCUMENT[_GETELEMENTBYID](s) : s;
},

_isArray = function(obj) {
  return _OP[_TOSTRING][_APPLY](obj) === '[object Array]';
},

_isUndefined = function(obj) {
  return typeof obj === _UNDEFINED;
},

_isNull = function(obj) {
  return obj === _NULL;
},

_isBoolean = function(obj) {
  return typeof obj === 'boolean';
},

_isFunction = function(obj) {
  return typeof obj === _FUNCTION;
},

_isNumber = function(obj) {
  return typeof obj === _NUMBER && isFinite(obj);
},

_isInt = function(obj) {
  var iV = parseInt(obj, 10);
  if (isNaN(iV)) {
    return _FALSE;
  }
  return obj==iV && obj[_TOSTRING]()==iV[_TOSTRING](); 
},

_isFloat = function(obj) {
  // T_T has problem detect 1.2e5
  var fV = parseFloat(obj);
  if (isNaN(fV)) {
    return _FALSE;
  }
  return obj==fV && _getReg('\\.').test(obj);
},

_isObject = function(obj) {
  return (obj && (typeof obj === 'object' || _isFunction(obj))) || _FALSE;
},

_isString = function(obj) {
  return typeof obj === _STRING;
},

_isEmpty = function(obj) {
  return obj===_NULL || typeof obj=== 'undefined' || obj === 0 || obj === _FALSE ||
    obj === '' || (typeof obj[_LENGTH] === _NUMBER && obj[_LENGTH] === 0);
},

_isNode = function(obj, strict) {
  return _isObject(obj) && ((!strict && (obj==_WINDOW || obj==_DOCUMENT)) ||
    obj.nodeType == _NODE_TYPE.ELEMENT_NODE);
},

/**
 * @param {array|object|object(to be traversed)} aAry
 * @param {function} fn callback function accept 2 params, array element value and index
 * @param {boolean} bIsObject if the aAry is not an array, false=treat as 1 element array, true=tranverse the object
 * @param {boolean} bNotCheckOwnProperty for isObject mode, default will do own property check, this param cancels check
 */
_each = function(aAry, fn) {
  var i,j, notCheckOwn = arguments[3] === _TRUE ?  _TRUE : _FALSE;
  if (!aAry) {
    return;
  }
  if (arguments[2]) {
    for(i in aAry) {
      if (notCheckOwn || aAry.hasOwnProperty(i)) {
        fn(aAry[i], i);
      }
    }
  } else {
    //  splice failed for Nodelist 
    //  if (typeof aAry.length !== 'number' || typeof aAry.splice !== 'function') 

    //form element has a length property -- tagName
    if (typeof aAry == _STRING || typeof aAry[_LENGTH] !== _NUMBER || aAry.tagName ||
      aAry === _WINDOW) {
      //not an array
      aAry = [aAry];
    }
    for(i=0,j=aAry[_LENGTH];i<j;++i) {
      fn(aAry[i], i);
    }
  }
},
/**
 * @param {integer} start the first number (could be ignored as 0)
 * @param {integer} end the last number < end
 * @param {integer} pace
 */
_range = function(start, end, pace) {
  pace = pace || 1;
  var aReturn = [];
  if (end === _UNDEFINED) {
    end = start;
    start = 0;
  }
  while(end > start) {
    aReturn[aReturn[_LENGTH]] = start;
    start += pace;
  }
  return aReturn;
},

/**
 * @param {array|object|object(to be traversed)} aAry
 * @param {function} fn callback function accept 2 params, array element value and index
 * @param {boolean} bIsObject if the aAry is not an array, false=treat as 1 element array, true=tranverse the object
 */
_all = function(aAry, fn, bIsObject) {
  var i, j;
  if (!aAry) {
    return ;
  }
  if (bIsObject) {
    for(i in aAry) {
       if (aAry.hasOwnProperty(i) && !fn(aAry[i], i)) {
         return _FALSE;
       }
    }
  } else {
    //  splice failed for Nodelist
    //  if (typeof aAry.length !== 'number' || typeof aAry.splice !== 'function') 
    if (typeof aAry == _STRING || typeof aAry[_LENGTH] !== _NUMBER || aAry.tagName ||
      aAry === _WINDOW) {
      //not an array
      aAry = [aAry];
    }
    for(i=0,j=aAry[_LENGTH];i<j;++i) {
       if (!fn(aAry[i], i)) {
         return _FALSE;
       }
    }
  }
  return _TRUE;
},

/**
 * @param {array|object|object(to be traversed)} aAry
 * @param {function} fn callback function accept 2 params, array element value and index
 * @param {boolean} bIsObject if the aAry is not an array, false=treat as 1 element array, true=tranverse the object
 */
_find = function(aAry, fn, bIsObject) {
  var i, j;
  if (!aAry) {
    return;
  }
  if (bIsObject) {
    for(i in aAry) {
      if (aAry.hasOwnProperty(i) && fn(aAry[i], i)) {
        return i;
      }
    }
  } else {
    if (typeof aAry == _STRING || typeof aAry[_LENGTH] !== _NUMBER || aAry.tagName ||
      aAry === _WINDOW) {
      aAry = [aAry];
    }
    for(i=0,j=aAry[_LENGTH];i<j;++i) {
      if (fn(aAry[i], i)) {
        return i;
      }
    }
  }
  return -1;
},

/**
 * @param {array|object|object(to be traversed)} aAry
 * @param {function} fn callback function accept 2 params, array element value and index
 * @param {boolean} bIsObject if the aAry is not an array, false=treat as 1 element array, true=tranverse the object
 */
_any = function(aAry, fn, bIsObject) {
  var i, j;
  if (!aAry) {
    return;
  }
  if (bIsObject) {
    for(i in aAry) {
      if (aAry.hasOwnProperty(i) && fn(aAry[i], i)) {
        return _TRUE;
      }
    }
  } else {
    if (typeof aAry == _STRING || typeof aAry[_LENGTH] !== _NUMBER || aAry.tagName ||
      aAry === _WINDOW) {
      aAry = [aAry];
    }
    for(i=0,j=aAry[_LENGTH];i<j;++i) {
      if (fn(aAry[i], i)) {
        return _TRUE;
      }
    }
  }
  return _FALSE;
},

/**
 * @param {function} fn callback function, accepts 3 params, base, array element value and index
 * @param {mixed} base 
 * @param {array|object} aAry accept an array or a single element
 * @param {boolean} bIsObj
 */
_reduce = function(aAry, fn, base, bIsObj) {
  _each(aAry, function(oItem, i){
    base = fn(base, oItem, i);
  }, bIsObj);
  return base;
},

/**
 * @param {array|object} aAry
 * @param {function} fn callback function, accept 2 params, array element value and index
 * @param {boolean} bIsObj
 */
_map = function(aAry, fn, bIsObj) {
  var aNew = [];
  _each(aAry, function(oItem, i) {
    aNew[i] = fn(oItem);
  }, bIsObj);
  return aNew;
},

/**
 * turn some thing in to an array
 * @param {fixed} aAry array-like object with a length attribute
 */
_a = function(aAry) {
  if (!aAry) {
    return [];
  }
  var leng = aAry[_LENGTH] || 0, results = new Array(leng);
  while (leng--) {
    results[leng] = aAry[leng];
  }
  return results;
},

/**
 * split a string with space
 * @param {string} str
 */
_w = function (str) {
  if (!_isString(str)) {
    return [];
  }
  str = _trim(str);
  return str ? str[_SPLIT](_getReg('\\s+')) : [];
},

/**
 * delete duplicated values in an array, get unique array
 * @param {array} aAry
 * @param {boolean} sorted indicated the array is already sorted or not
 */
_unique = function(aAry, sorted) {
  return _reduce(aAry, function(array, value, index) {
    if (0 === index ||
      (sorted ? array[array[_LENGTH]-1] != value : !_inArray(value, array, _TRUE))) {
      array.push(value);
    }
    return array;
  }, []);
},

/**
 * return false or an array of index of the found values
 */
_inArray = function(obj, ary, strict) {
  var found = _FALSE;
  _each(ary, function(oItem, i) {
    if(oItem===obj || (!strict && oItem==obj)) {
      found = found === _FALSE ? found = [] : found;
      found[found[_LENGTH]] = i;
    }
  });
  return found;
},

/**
 * delete part of an array with high efficiency
 * Based on John Resig's Array Remove
 * @param {array} ary
 * @param {integer} from the start index
 * @param {integer} to the end index
 */
_arrayRemove = function(ary, from, to) {
  var rest, len=ary[_LENGTH];
  from = from<0 ? from-0+len : from;
  to = to<0 ? to-0+len : to;
  if (to<from || from>=len || to>=len) {
    return _FALSE;
  }
  rest = ary.slice((to || from) + 1 || ary[_LENGTH]);
  ary[_LENGTH] = from < 0 ? ary[_LENGTH] + from : from;
  return ary.push[_APPLY](ary, rest);
},

/**
 * function to get a property of an array of object in to an new array
 * @param {array} aAry 
 * @param {string} key
 */
_absorb = function(aAry, key) {
  var aValues = [];
  _each(aAry, function(o) {
    aValues.push(o[key]);
  });
  return aValues;
},


/**
 * function to create function with prefilled param
 * @param {function|string} fn string must be a _OPER index
 */
_curry = function(fn) {
  fn = typeof fn == _STRING && _OPER[fn] ? _OPER[fn] : fn;
  var argu = _a(arguments);
  argu.shift();
  return function() {
    return fn[_APPLY](_NULL, argu.concat(_a(arguments)));
  };
},

/**
 * function to compose 2 functions
 * @param {function} fnWrap 
 * @param {function} fnParam 
 */
_compose = function(fnWrap, fnParam) {
  return function() {
    return fnWrap(fnParam[_APPLY](_NULL, arguments));
  };
},

/**
 * function to generate a negative functions
 * @param {function} fn 
 */
_negate = function(fn) {
  return function() {
    return !fn[_APPLY](_NULL, arguments);
  };
},

/**
 * function to get the keys of an object as an array
 * @param {object} aAry 
 */
_keys = function(aAry) {
  var aKeys = [];
  _each(aAry, function(o,i) {
    aKeys.push(i);
  }, _TRUE);
  return aKeys;
},

/**
 * function to get the values of an object as an array
 * @param {object} aAry 
 */
_values = function(aAry) {
  var aValues = [];
  _each(aAry, function(o,i) {
    aValues.push(o);
  }, _TRUE);
  return aValues;
},


/**
 * function to clone an object
 * @param {object} obj 
 */
_clone = function (obj) {
  if(obj === _NULL || typeof(obj) != 'object') {
    return obj;
  }

  var temp = new obj.constructor();
  for(var key in obj) {
    if (obj.hasOwnProperty(key)) {
      temp[key] = _clone(obj[key]);
    }
  }

  return temp;
},

/**
 * @param {object} r reciever
 * @param {object} s source 
 * @param {boolean} override true to override properties with the same name in r, default to true
 */
_objExtend = function(r, s, override) {
  override = override === _FALSE ? _FALSE : _TRUE;
  for (var i in s) {
    if(override || !(i in r)) {
      r[i] = s[i];
    }
  }
},


_getMergedObj = function(oldOne, newOne) {
  var _newO = {}, i;
  for(i in oldOne) {
    if (oldOne.hasOwnProperty(i)) {
      _newO[i] = oldOne[i];
    }
  }
  for(i in newOne) {
    if (newOne.hasOwnProperty(i)) {
      _newO[i] = newOne[i];
    }
  }
  return _newO;
},

/**
 * from http://blog.stevenlevithan.com/archives/faster-trim-javascript
 * faster trim a string
 */
_trim = function(str) {
  str = str.replace(_getReg('^\\s\\s*'), '');
  var ws = _getReg('\\s'),
    i = str[_LENGTH];
  while (ws.test(str.charAt(--i))) {
  }
  return str.slice(0, i + 1);
},

/**
 * function to cut a string to certain length and append truncation
 * @param {string} str the string to be cutted
 * @param {integer} len the length to be cutted to, include truncation, default 30
 * @param {string} truncation the string to be appended at the end, default '...'
 * @param {boolean} isAsciiLen set to true means the second param is ascii character length,
 *                  a non-ascii character will be count as 2 ascii length
 */
_truncate = function(str, len, truncation, isAsciiLen) {
  var i, j = str[_LENGTH], k, actualLen = 0;

  len = len || 30;
  truncation = truncation === _UNDEFINED ? '...' : truncation;
  isAsciiLen = isAsciiLen === _UNDEFINED ? false : true;


  if (isAsciiLen && j > len / 2) {

    for (i=0,k=truncation[_LENGTH]; i<k; i++) {
      actualLen += truncation.charCodeAt(i) > 255 ? 2 : 1;
    }

    for (i=0; i<j; i++) {
      actualLen += str.charCodeAt(i) > 255 ? 2 : 1;
      if (actualLen > len) {
        break;
      }
    }

    str = str.substring(0, i) + truncation;
  } else if (str[_LENGTH] > len) {
    str = str.slice(0, len - truncation[_LENGTH]) + truncation;
  } else {
    str = str + '';
  }
  return str;
},

_strRepeat= function(str, times) {
  var result = [];
  _each(_range(times), function() {
    result[result[_LENGTH]] = str;
  });
  return result.join('');
},

_baseConvert = function(num, to, from) {
  if (!num || !to) {
    return _FALSE;
  }
  num = String(num).toLowerCase();
  if (from === _UNDEFINED) {
    if (_startWith(num, '0x')) {
      from = 16;
    } else if (_startWith(num, '0')) {
      from = 8;
    } else {
      from = 10;
    }
  }
  num = parseInt(num, from);
  return num[_TOSTRING](to);
},

_stripTags = function(str) {
  return str.replace(_getReg('<\\/?[^>]+>', 'gi'), '');
},

_camelize = function(str) {
  var parts = str[_SPLIT]('_'), len = parts[_LENGTH];
  if (len == 1) {
    return parts[0];
  }

  var camelized = str.charAt(0) == '_' ?
    parts[0].charAt(0).toUpperCase() + parts[0].substring(1) : parts[0];

  for (var i = 1; i < len; ++i) {
    camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
  }

  return camelized;
},

_undersore = function(str) {
  return str.replace(_getReg('([A-Z]+)([A-Z][a-z])', 'g'), '$1_$2').replace(_getReg('([a-z\\d])([A-Z])', 'g'), '$1_$2').toLowerCase();
},

_capitalize = function(str) {
  return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
},

/**
 * function to print a string from template
 * @param {string} sTemplate a string with '%s' in it
 * @param {string} replacement can be many replacement parameters replace '%s' in template in order
 */
_sprintf = function(sTemplate) {
  var i = 1;
  while (arguments[i] !== _UNDEFINED) {
    sTemplate = sTemplate.replace(_getReg('%s'), arguments[i++]);
  }
  return sTemplate;
},

_startWith = function(str, pattern) {
  return str[_INDEXOF](pattern) === 0;
},

_endWith = function(str, pattern) {
  var d = str[_LENGTH] - pattern[_LENGTH];
  return d >= 0 && str.lastIndexOf(pattern) === d;
},

_greplace = function (str, sReg, fn) {
  var gReg = _getReg(sReg, 'g'), nReg = _getReg(sReg), p, result = '';
  _each(str.match(gReg), function(o, i) {
    p = str[_INDEXOF](o)+o[_LENGTH];
    result += str.substring(0, p).replace(nReg, fn(o, i));
    str = str.substring(p);
  });
  return result + str;
},

_rand = function(min, max) {
  if (max === _UNDEFINED) {
    max = min;
    min = 0;
  }
  return Math.round(Math.random()*(max - min)) + min;
},

_getReg = (function() {
  var regCache = {};
  return function(str, flag) {
    var key = str + (flag || '');
    if (!regCache[key]) {
      regCache[key] = new RegExp(str, flag);
    }
    return regCache[key];
  };
}()),

_log = (function() {
  var starting = 0, fn, count = 0;
  if (_WINDOW.console) {
    fn = function() {
      if (_CFG[1] == _FALSE) {
        return;
      }
      var nt = (new Date()).getTime(), oldst, c =_WINDOW.console, args;
      oldst = starting || nt;
      starting = nt;
      nt = (++count)+',('+ nt +',+'+ (nt - oldst) +'ms): ';
      args = Array[_PROTOTYPE].slice[_APPLY](arguments);
      args.unshift(nt);
      if (c.log[_APPLY]) {
        c.log[_APPLY](c, args);
      } else {
        c.log(args.join(' '));
      }
    };
  } else {
    fn = _EF;
  }
  return fn;
}());


_SELF.add('Lang', {
  EF : _EF,
  K : _K,
  NODE_TYPE : _NODE_TYPE,
  //type
  OPER : _OPER, //object
  isArray : _isArray, // function(obj)
  isUndefined : _isUndefined, // function(obj)
  isNull : _isNull, // function(obj)
  isBoolean : _isBoolean, // function(obj)
  isFunction : _isFunction, // function(obj)
  isNumber : _isNumber, // function(obj)
  isInt : _isInt, // function(obj)
  isFloat : _isFloat, // function(obj)
  isObject : _isObject, // function(obj)
  isString : _isString, // function(obj)
  isEmpty : _isEmpty, // function(obj)
  isNode : _isNode, // function(obj, strict)

  //array
  each : _each, // function(aAry, fn, bIsObject)
  e : _each, // alias for each
  range : _range, // function(start , end, pace)
  all : _all, // function(aAry, fn, bIsObject)
  find : _find, // function(aAry, fn, bIsObject)
  any : _any, // function(aAry, fn, bIsObject)
  reduce : _reduce, // function(aAry, fn, base, bIsObject)
  map : _map, // function(aAry, fn, bIsObj)
  a : _a, // function(aAry) array clone
  w : _w, // function(str)
  unique : _unique, // function(aAry, sorted)
  inArray : _inArray, // function(obj, ary, strict)
  arrayRemove : _arrayRemove, // function(ary, from, to)
  absorb : _absorb, // function(aAry, key)

  //function
  curry : _curry, // function(fn, [parms,...])
  compose : _compose, // function(fnWrap, fnParam)
  negate : _negate, // function(fn)

  //object
  keys : _keys, // function(aAry)
  values : _values, // function(aAry)
  clone : _clone, // function(fn)
  objExtend : _objExtend, // function(r, s, override)
  getMergedObj : _getMergedObj, // function(oldOne, newOne)

  //string
  trim : _trim, // function(str)
  truncate: _truncate, // function(str, len, truncation)
  strRepeat : _strRepeat, // function(str, times)
  baseConvert : _baseConvert, // function(num, to, from)
  stripTags : _stripTags, // function(str)
  camelize : _camelize, // function(str)
  underscore : _undersore, // function(str)
  capitalize : _capitalize, // function(str)
  sprintf : _sprintf, // function(sTemplate, [str...])
  startWith : _startWith, // function(str, pattern)
  beginWith : _startWith, // function(str, pattern)
  endWith : _endWith, // function(str, pattern)
  greplace : _greplace, // function (str, sReg, fn)

  rand : _rand, //function(int)
  getReg : _getReg
});

_SELF.add('Log', _log);



_SELF.add('Dom', {
  $ : function(s) {
    var el = _id(s);
    return el ? [el] : [];
  },
  f : _id,
  hasClass : function(el, cls) {
    return _getReg('(?:^|\\s+)' + cls + '(?:\\s+|$)').test(el.className);
  },
  addClass : function(el, cls) {
    if (!_SELF.Dom.hasClass(el, cls)) {
      el.className = _trim([el.className, cls].join(' '));
    }
  },
  remClass : function(el, cls) {
    var d = _SELF.Dom;
    if (cls && d.hasClass(el, cls)) {
      el.className = _trim(el.className.replace(_getReg('(?:^|\\s+)' +
                    cls + '(?:\\s+|$)'), ' '));
      if ( d.hasClass(el, cls) ) {
        d.remClass(el, cls);
      }
    }
  },
  getElementsByClassName : (function() {
    return _DOCUMENT[_GETELEMENTSBYCLASSNAME]?
      function(node, cls) {
        return node[_GETELEMENTSBYCLASSNAME](cls);
      } :
      function(node, cls) {
        var classes = cls[_SPLIT](" "),
          classesToCheck = [],
          elements = node.all ? node.all : node[_GETELEMENTSBYTAGNAME]('*'),
          current,
          returnElements = [],
          match;
        for(var k=0, kl=classes[_LENGTH]; k<kl; k+=1){
          classesToCheck.push(_getReg("(^|\\s)" + classes[k] + "(\\s|$)"));
        }
        for(var l=0, ll=elements[_LENGTH]; l<ll; l+=1){
          current = elements[l];
          match = _FALSE;
          for(var m=0, ml=classesToCheck[_LENGTH]; m<ml; m+=1){
            match = classesToCheck[m].test(current.className);
            if (!match) {
              break;
            }
          }
          if (match) {
            returnElements.push(current);
          }
        }
        return returnElements;
      };
  }()),

  insertBefore : function(newOne, oldOne) {
    if (oldOne.parentNode && oldOne.parentNode.insertBefore) {
      oldOne.parentNode.insertBefore(newOne, oldOne);
    }
  },

  insertAfter : function (newNode,oldNode) {
    if (oldNode.nextSibling) {
      oldNode.parentNode.insertBefore(newNode, oldNode.nextSibling);
    } else {
      oldNode.parentNode[_APPENDCHILD](newNode);
    }
  },

  /**
   * create (append) html element by object
   * @param {object} obj
   * @config {string} tag default div
   * @config {string|array} cls
   * @config {string} id
   * @config {string} html innerHTML
   * @config {string} css
   * @config {object|array} child child node
   * @config {object} attrs attrs to be copied to the element
   * @config {string} text create a text node
   * config {string} src use attrs instead
   * config {object} parentN deprecated!!
   * @param {object|string} oparent
   */
  addEl : function(obj, oparent) {
    // tag, cls, id, html, css, child, attrs, text, value, src, name
    if (!obj) {
      return _FALSE;
    }
    var el,
      tagName = obj.tag,
      tmp, value, fragment,
      _addClass = function(oItem) {
        _SELF.Dom.addClass(el, oItem);
      },
      _fnAddEl = function(oItem) {
        _SELF.Dom.addEl(oItem, el);
      };

    if (_OP[_TOSTRING][_APPLY](obj) == _ARRAY_TYPE) {
      fragment = _DOCUMENT.createDocumentFragment();
      _SELF.Lang.e(obj, function(o) {
        _SELF.Dom.addEl(o, fragment);
      });
      oparent = _id(oparent);
      if (oparent) {
        fragment = oparent[_APPENDCHILD](fragment);
      }
      return fragment;
    }

    if (!tagName && oparent && oparent.tagName) {
      switch (oparent.tagName.toLowerCase()) {
        case 'table' :
        case 'tbody' :
          tagName = 'tr';
          break;
        case 'tr' :
          tagName = 'td';
          break;
        case 'ul' :
          tagName = 'li';
          break;
        case 'select' :
          tagName = 'option';
          break;
      }
    }

    tagName = tagName || 'div';

    if (_SELF.cfg[4].ie && (tagName == 'input' || tagName == 'select') && 'name' in obj) {
      el = _DOCUMENT.createElement('<input name='+obj.name+'>');
    } else if (tagName == '_text') {
      el = _DOCUMENT.createTextNode(obj.text);
      obj = {};
    } else {
      el = _DOCUMENT.createElement(tagName);
    }
    if(!el) {
      return _FALSE;
    }

    for(tmp in obj) {
      if (obj.hasOwnProperty(tmp)) {
        value = obj[tmp];
        switch(tmp) {
        case 'cls' :
          _SELF.Lang.e(value, _addClass);
          break;
        case 'child' :
          _SELF.Lang.e(value, _fnAddEl);
          break;
        case 'text' :
          el[_APPENDCHILD](_DOCUMENT.createTextNode(value));
          break;
        case 'css' :
          el.style.cssText = value;
          break;
        case 'html' :
          el.innerHTML = value;
          break;
        default :
          el[tmp] = value;
          break;
        }
      }
    }

    oparent = _SELF.Dom.f(oparent);
    if (oparent && oparent[_APPENDCHILD]) {
      el = oparent[_APPENDCHILD](el);
    }

    return el;
  },

  remEl : function(els) {
    var rmFn = function(el) {
      el = _SELF.Dom.$(el);
      _each(el, function(o) {
        if (o && o.parentNode) {
          o.parentNode.removeChild(o);
        }
      });
    };
    if(_isArray(els)) {
      for(var i=els[_LENGTH]-1;i>-1;i--) {
        rmFn(els[i]);
      }
    } else {
      rmFn(els);
    }
  },

  addText : function(obj, txt) {
    obj[_APPENDCHILD](_DOCUMENT.createTextNode(txt));
  },

  /**
   * dynamically add a css stylesheet
   * @param {string} sText the css content
   */
  addStyle : function(sText) {
    if (_DOCUMENT.createStyleSheet) {
      //IE, create new css
      //!!remember the 31 limit
      var dCss = _DOCUMENT.createStyleSheet('');
      dCss.cssText = sText;
    } else {
      this.addEl({
        tag : 'style',
        textContent : sText
   //     html : sText
      }, _DOCUMENT[_GETELEMENTSBYTAGNAME]('head')[0]);
    }
  },

  getScrollXY : function() {
    var top = Math.max(_DOCUMENTELEMENT.scrollTop, _DOCUMENT.body.scrollTop),
      left = Math.max(_DOCUMENTELEMENT.scrollLeft, _DOCUMENT.body.scrollLeft);
    return [left, top];
  },

  getPosition : function(obj, type) {
    //offsetLeft or offsetTop
    var distance = 0;
    while (!_isEmpty(obj.offsetParent)) {
      distance += obj[type];
      obj = obj.offsetParent;
    }
    distance += _DOCUMENT.body[type] - 0;
    return distance;
  },

  /**
   * @param {string|array} sQuery an Id or a CSS selector string
   */
  v : function(sQuery) {
    var o = this.f(sQuery);
    if (o) {
      return o.value;
    }
    return _FALSE;
  },

  getWindowXY : function() {
    var height = self.innerHeight, // Safari, (Opera)
      width = self.innerWidth,
      db = _DOCUMENT.body,
      mode = _DOCUMENT[_COMPAT_MODE];
    if ( (mode || _CFG[4].ie)) { // IE, Gecko, (Opera)
      if (mode == _CSS1_COMPAT) { // Standards
        height = _CFG[4].opera ? height : _DOCUMENTELEMENT.clientHeight;
        width = _DOCUMENTELEMENT.clientWidth;
      } else { // Quirks
        height = _CFG[4].opera ? height : db.clientHeight;
        width = db.clientWidth;
      }
    }
    return [width, height];
  },

  getDocumentXY : function() {
    var db = _DOCUMENT.body,
      scrollWidth = (_DOCUMENT[_COMPAT_MODE] != _CSS1_COMPAT || _CFG[4].webkit) ?
        db.scrollWidth : _DOCUMENTELEMENT.scrollWidth,
      scrollHeight = (_DOCUMENT[_COMPAT_MODE] != _CSS1_COMPAT || _CFG[4].webkit) ?
        db.scrollHeight : _DOCUMENTELEMENT.scrollHeight,
      wxy = _SELF.Dom.getWindowXY();

    scrollWidth = Math.max(scrollWidth, wxy[0]);
    scrollHeight = Math.max(scrollHeight, wxy[1]);
    return [scrollWidth, scrollHeight];
  },


  /**
   * set an element to the window center
   * @param {object} el the dom element
   */
  setCenter : function(el) {
    var wXY = this.getWindowXY(),
      sXY = this.getScrollXY(),
      w = el.offsetWidth || el.clientWidth || parseInt(el.style.width, 10),
      h = el.offsetHeight || el.clientHeight || parseInt(el.style.height, 10),
      top;

    el.style.position = 'absolute';
    el.style.left = (wXY[0] - w) / 2 + sXY[0] + 'px';

    top  = (wXY[1] - h) / 2 + sXY[1];
    el.style.top = (top < 50 ? 50 : top) + 'px';
  }

});

/**
 * will override __.Dom.$, __.Dom.f
 * @module Selector
 * @requires Dom,Lang
 */
_SELF.add('Selector', (function() { // depends on Dom & Lang
  var _D = _SELF.Dom, _L = _SELF.Lang,
  _contains, _cssQuery;

  /**
   * private function used to get Dom element
   * @param {string|array} sQuery an Id or a CSS selector string
   * @param {object} root dom reference of the root
   * @param {boolean} firstOnly stop after found the first el
   * @return {array} array of Dom elements
   */
  _D.$ = function(sQuery, root, firstOnly) {
    var _els, i, j;
    if (sQuery) {
      if (sQuery.nodeType) { // Node
        _els = [sQuery];
      } else if (sQuery.item) { // NodeList
        _els = [];
        for (i=0,j=sQuery[_LENGTH];i<j;++i) {
          _els[_els[_LENGTH]] = sQuery[i];
        }
      } else if (typeof sQuery === _STRING) { // id or query
        _els = root ? _NULL : _DOCUMENT[_GETELEMENTBYID](sQuery);
        _els = _els ? [_els] : _cssQuery(sQuery, root, firstOnly);
      }
    } else {
      _els = [];
    }
    return _els;
  };

  /**
   * first
   * @param {string|array} sQuery an Id or a CSS selector string
   * @param {string|dom} root the rood id or dom reference
   */
  _D.f = function() {
    return _D.$[_APPLY](_D, arguments)[0];
  };

  /**
   * function to test whether the dom needle is in haystack
   * @param {object} needle
   * @param {object} haystack
   */
  _contains = (function() {
    if (_DOCUMENTELEMENT.contains)  {
      return function(needle, haystack) {
        if (!needle || !haystack || !needle.tagName || ! haystack.tagName) {
          return false;
        }
        return haystack.contains(needle);
      };
    } else if (_DOCUMENTELEMENT.compareDocumentPosition) {
      return function(needle, haystack) {
        if (!needle || !haystack || !needle.tagName || ! haystack.tagName) {
          return false;
        }
        return !!(haystack.compareDocumentPosition(needle) & 16);
      };
    }
  }());

  _D.contains = _contains;

  function _nodeFilter(els, config) {
    var leftNode, tagName, i, len, classes, k, kl, classesToCheck , match, current;

    els = _L.unique(els);
    if (config.tagname) {
      leftNode = [];
      tagName = _getReg("\\b" + config.tagname + "\\b", "i");
      for (i=0,len=els[_LENGTH];i<len;++i) {
        if (tagName.test(els[i].tagName)) {
          leftNode.push(els[i]);
        }
      }
      els = leftNode;
    }
    if (config.classname) {
      leftNode = [];
      classes = config.classname[_SPLIT](' ');
      classesToCheck = [];

      for (i=0,len=classes[_LENGTH];i<len;++i) {
        classesToCheck.push(_getReg("(^|\\s)" + classes[i] + "(\\s|$)"));
      }
      for (i=0,len=els[_LENGTH];i<len;++i) {
        current = els[i];
        match = _FALSE;
        for (k=0,kl=classesToCheck[_LENGTH];k<kl;k++) {
          match = classesToCheck[k].test(current.className);
          if (!match) {
            break;
          }
        }
        if (match) {
          leftNode.push(current);
        }
      }
      els = leftNode;
    }
    return els;
  }

  _cssQuery = (function() {
    return _DOCUMENT.querySelectorAll ?
    function (selector, root, firstOnly) {
      var result = [], i, len, nodelist;
      if (root && !root.nodeName) {
        root = _D.f(root);
        if (!root) {
          return result;
        }
      }
      root = root || _DOCUMENT;
      if (firstOnly) {
        result[0] = root.querySelector(selector);
      } else {
        nodelist = root.querySelectorAll(selector);
        for (i=0,len=nodelist[_LENGTH];i<len;++i) {
          result.push(nodelist[i]);
        }
      }
      return result;
    } :
    function (selector, root, firstOnly) {
      var result = [], groups, found, i, len, roots, levels, thetag, theid, theclass,
      nodes, classstr, j, jlen, theroot, bcontain, directChild, thesel, tmpnodes, k, klen;
      if (!selector) {
        return result;
      }
      selector = _L.trim(selector);
      groups = _L.trim(selector)[_SPLIT](',');
      if (groups[_LENGTH]> 1) {
        for (i = 0, len = groups[_LENGTH]; i < len; ++i) {
          found = _cssQuery(groups[i], root, firstOnly);
          if (firstOnly && found[_LENGTH]> 1) {
            result[0] = found[0];
            break;
          } else {
            result = result.concat(found);
          }
        }
        return result;
      }
      if (root && !root.nodeName) {
        root = _D.f(root);
        if (!root) {
          return result;
        }
      }
      root = root || _DOCUMENT;

      roots = [root]; //for multilevel selector

      levels = selector[_SPLIT](' ');
      directChild = _FALSE;
      for (j=0,jlen=levels[_LENGTH];j<jlen;j++) {
        thesel = levels[j];
        if (thesel == '>') {
          directChild = _TRUE;
          continue;
        }
        nodes = [];
        thetag = thesel.match(_getReg('^([^.#]+)'));
        theid = thesel.match(_getReg('#([^.#]+)'));
        theclass = thesel.match(_getReg('\\.[^.#]+', 'g'));
        classstr = theclass ? theclass.join(' ').replace(_getReg('\\.', 'g'), '') : '';
        if (theid) {
          nodes = _DOCUMENT[_GETELEMENTBYID](theid[1]);
          nodes = nodes ? [nodes] : [];
          thetag = thetag ? thetag[1] : _NULL;
        } else if (thetag || theclass) {
          for (i=0,len=roots[_LENGTH];i<len;++i) {
            tmpnodes = thetag ? roots[i][_GETELEMENTSBYTAGNAME](thetag[1]) :
              _D[_GETELEMENTSBYCLASSNAME](roots[i], classstr);
            if (directChild) {
              for (k=0,klen=tmpnodes[_LENGTH];k<klen;k++) {
                if (tmpnodes[k].parentNode === roots[i]) {
                  nodes.push(tmpnodes[k]);
                }
              }
            } else {
              nodes = nodes.concat(_L.a(tmpnodes));
            }
          }
          classstr = thetag ? classstr : _NULL;
          thetag = _NULL;
        }

        nodes = _nodeFilter(nodes, {
          tagname : thetag,
          classname : classstr
        });

        if (theid && nodes[0]) {
          bcontain = _FALSE;
          for (i=0,len=roots[_LENGTH];i<len;++i) {
            theroot = roots[i];
            if (directChild ? nodes[0].parentNode === theroot :
              (theroot == _DOCUMENT || _contains(nodes[0], theroot))) {
              bcontain = _TRUE;
              break;
            }
          }
          if (!bcontain) {
            nodes = [];
          }
        }
        directChild = _FALSE;
        roots = nodes;
        if (roots[_LENGTH] === 0) {
          break;
        }
      }
      return roots;
    };
  }());

  return {
    query : _cssQuery
  };

}()));

_SELF.add('Event', (function() {
  var _on = function () {
    if (_WINDOW.addEventListener) {
      return function(el, type, fn, capture) {
        el = typeof el == _STRING ? _SELF.Dom.$(el) : el;
        type = type && type[_INDEXOF](':') > -1 ? _DATAAVAILABLE : type;
        _each(el, function(o) {
          _SELF.Event.listeners++;
          o.addEventListener(type, fn, (!!capture));
        });
      };
    } else if (_WINDOW.attachEvent) {
      return function(el, type, fn) {
        el = typeof el == _STRING ? _SELF.Dom.$(el) : el;
        type = type && type[_INDEXOF](':') > -1 ? _DATAAVAILABLE : type;
        _each(el, function(o) {
          _SELF.Event.listeners++;
          o.attachEvent("on" + type, fn);
        });
      };
    } else {
      return function(){};
    }
  }(),

  _off = function() {
    if (_WINDOW.removeEventListener) {
      return function (el, type, fn, capture) {
        el = typeof el == _STRING ? _SELF.Dom.$(el) : el;
        type = type && type[_INDEXOF](':') > -1 ? _DATAAVAILABLE : type;
        _each(el, function(o) {
          _SELF.Event.listeners--;
          o.removeEventListener(type, fn, !!capture);
        });
      };
    } else if (_WINDOW.detachEvent) {
      return function (el, type, fn) {
        el = typeof el == _STRING ? _SELF.Dom.$(el) : el;
        type = type && type[_INDEXOF](':') > -1 ? _DATAAVAILABLE : type;
        _each(el, function(o) {
          _SELF.Event.listeners--;
          o.detachEvent("on" + type, fn);
        });
      };
    } else {
      return function(){};
    }
  }(),

  /**
   * fire custom event
   */
  _fire = function(el, eventName, memo) {
    /* example:
     _.on(document, 'st:ohyeah', function(e) {
       if (e.eventName == 'st:ohyeah') {
         alert(e.memo.haha)
       }
     })
     _.fire(doucment, 'st:ohyeah', {'haha':123123})
    */
    var element = _SELF.Dom.f(el) || _DOCUMENT;
    if (element == _DOCUMENT && _DOCUMENT.createEvent && !element.dispatchEvent) {
      element = _DOCUMENT.documentElement;
    }

    var ev;
    if (_DOCUMENT.createEvent) {
      ev= _DOCUMENT.createEvent("HTMLEvents");
      //initEvent function(type, bubbles, cancelable) 
      ev.initEvent(_DATAAVAILABLE, _TRUE, _TRUE);
    } else {
      ev= _DOCUMENT.createEventObject();
      ev.eventType = 'on'+ _DATAAVAILABLE;
    }

    ev.eventName = eventName;
    ev.memo = memo || { };

    if (_DOCUMENT.createEvent) {
      element.dispatchEvent(ev);
    } else {
      element.fireEvent(ev.eventType, ev);
    }
    return ev;
  },

  _stopPropagation = function(ev) {
    if (ev.stopPropagation) {
      ev.stopPropagation();
    } else {
      ev.cancelBubble = _TRUE;
    }
  },

  _preventDefault = function(ev) {
    if (ev.preventDefault) {
      ev.preventDefault();
    } else {
      ev.returnValue = _FALSE;
    }
  },

  _stopEvent = function(ev) {
    _preventDefault(ev);
    _stopPropagation(ev);
  },
  // http://www.dynamic-tools.net/toolbox/isMouseLeaveOrEnter/
  /*
  _isMouseLeaveOrEnter = function (e, el) {
    if (e.type != 'mouseout' && e.type != 'mouseover') return false;
    var reltg = e.relatedTarget ? e.relatedTarget :
      e.type == 'mouseout' ? e.toElement : e.fromElement;
    while (reltg && reltg != el) reltg = reltg.parentNode;
    return (reltg != el);
  }
  ,
  */

  _readyList = [],
  _isReady = _FALSE,
  _isBound = _FALSE,
  _domReadyId,
  _bindReady = function() {
    var oldonload, ol = 'onload';
    if (_isBound) {
      return;
    }
    _isBound = _TRUE;

    //dom ready may be already fired when run to here
    if (_CFG[4].webkit) {
      if (_DOCUMENT.readyState == 'complete') {
        _hlReady();
      }
    }

    if (_CFG[4].ie) {
      _domReadyId = setInterval(function() {
        try {
          _DOCUMENT.documentElement.doScroll('left');
          clearInterval(_domReadyId);
          _domReadyId = _NULL;
          _hlReady();
        } catch (ex) {}
      }, 50);
    } else {
      _DOCUMENT.addEventListener("DOMContentLoaded", _hlReady, _FALSE);
    }
    oldonload = _WINDOW[ol];
    if (typeof oldonload != _FUNCTION) {
      _WINDOW[ol] = _hlReady;
    } else {
      _WINDOW[ol] = function() {
        _hlReady();
        oldonload();
      };
    }
  },
  _hlReady = function() {
    if(!_isReady) {
      _isReady = _TRUE;
      _each(_readyList, function(o) {
        o();
      });
      _readyList = _NULL;
    }
  },
  _onReady = function(fn) {
    _bindReady();
    if (_isReady) {
      fn.call(_WINDOW);
    } else {
      _readyList.push(function() {
        return fn.call(_WINDOW);
      });
    }
  },

  _bind = function(fn, obj) {
    return function() {
      fn.apply(obj, _a(arguments));
    };
  };

  return {
    /**
     * listeners count
     */
    listeners : 0,
    on : _on, // function(el, sType, fn, obj, override)
    off : _off, // function(el, sType, fn)
    fire : _fire, // function(el, eventName, memo)
    stopEvent : _stopEvent, // function(ev)
    stopPropagation: _stopPropagation, // function(ev)
    preventDefault: _preventDefault, // function(ev)
    //getEvXY: _getEvXY, // function(ev)
    onReady : _onReady, // function(fn)
    bind : _bind
  };
}()));

/**
 * function to start a ajax request
 * @module Io
 */
_SELF.add('Io', (function() {
  var onsucess = 'onSuccess',
  _log = _SELF.log,
  onfailure = 'onFailure',
  /**
   * function to start a ajax request
   * @param {string} sUrl the url to send xhr request
   * @param {object} config the config object
   * @config {function} onSuccess the onsuccess handler
   * @config {function} onFailure the onfail handler
   * @config {integer} timeout the timeout milliseconds
   * @param {string|object} the post string or object, object will be auto json encoded
   *                        ignore oPost param to start a GET request
   * @return {array} array[0] is the xhr instance, array[1] is the callback object
   */
  _ajax = (function() {
    var ajaxId = 0,
    _getTransport = function() {
      var i, j, t, transports = [
        function() {return new XMLHttpRequest();},
        function() {return new ActiveXObject('Microsoft.XMLHTTP');},
        function() {return new ActiveXObject('Msxml2.XMLHTTP');}
      ];
      for (i=0,j=transports[_LENGTH];i<j;++i) {
        try {
          t = transports[i]();
          if (t) {
            _getTransport = transports[i];
            return t;
          }
        } catch (e) {}
      }
    },
    ajax = function(url, config, oPost) {
      var newO, xh, cb, sType, oAr, id;
      if (oPost === _UNDEFINED) {
        sType = 'GET';
      } else {
        sType = 'POST';
        oPost = _SELF.Lang.isString(oPost) ? oPost : _SELF.Json.toJson(oPost);
      }

      config.__du_ajax_id = ajaxId++;

      config[onsucess] = onsucess in config ? config[onsucess] :
        function(o) {
          _SELF.log("Success!\nServer returns:" + o.responseText);
        };

      config[onfailure] = onfailure in config ? config[onfailure] :
        function(o) {
          _SELF.log("xmlhttp request Failed!");
        };

      //set default timeout to 60 seconds
      config.timeout = config.timeout ? config.timeout : 60000;

      xh = _getTransport();

      cb = function () {
        if(xh.readyState == 4 && xh.status == 200) {
          config[onsucess](xh.responseText, xh);
          xh = null;
        } else if (xh.readyState == 4) {
          config[onfailure](xh);
          xh = null;
        }
      };
      xh.onreadystatechange = cb;
      xh.open(sType, url, _TRUE);
      xh.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
      //xh.setRequestHeader("Content-Type", "text/plain");
      xh.send(oPost);
      cb = _NULL;
      config.xhr = xh;
      return config;
    };

    return ajax;
  }()),

  _ajaxManager = (function() {
    /**
     * @param {_Req} obj
     */
    var _fnFinish = function(obj) {
      var __aTempReq = [];
      _L.each(_aRequesting, function(o, i) {
        if (o[0].id !== obj.id) {
          __aTempReq.push(o);
        }
      });
      _aRequesting = __aTempReq;
      _sendRequest();
    },
    _doAjax = function(obj) {
      var od = obj.detail;
      od[onsucess] = function(t, o) {
        _fnFinish(obj);
        if (od.target) {
          _SELF.Dom.f(od.target).innerHTML = t;
        }
        if (od.onOk) {
          od.onOk(t, o);
        }
      };
      od[onfailure] = function(t, o) {
        _log('xmlhttp request #' + obj.id + ' failed');
        _fnFinish(obj);
        if(od.target) {
          _SELF.Dom.f(od.target).innerHTML = 'xmlhttp request #' + obj.id + ' failed';
        }
        if (od.onFail) {
          od.onFail(t, o);
        }
      };
      return _ajax(_makeUrl(od.sUrl, od.oGet, od.cache), od, od.sPost);
    },

    //array of _Req
    _aQueue = [],

    _iAjaxThread = _CFG[4].ie && _CFG[4].ie < 8 ? 2 : 4,

    //0:_Req, 1:connection, 2:ocallback
    _aRequesting = [],
    _iId = 0,
    _makeUrl = function(sUrl, oGet, bCache) {
      if (sUrl[_INDEXOF]('?') < 0) {
        sUrl += '?';
      }
      if (oGet) {
        _L.each(oGet, function(v,n) {
          sUrl += ['&', encodeURIComponent(n), '=', encodeURIComponent(v)].join('');
        }, _TRUE);
      }
      /*
      if (!bCache) {
        sUrl += '&.strand=' + Math.random();
      }*/
      return encodeURI(sUrl);
    },

    _sendRequest = function() {
      if (_aRequesting[_LENGTH] >= _iAjaxThread) {
        //waiting
        return;
      }
      var newReq = _aQueue.shift();
      if (!newReq) {
        return;
      }
      _aRequesting.push([newReq, _doAjax(newReq)]);
    },

    /**
     * constructor of the queueing ajax request object
     * @constructor
     */
    _Req = function(oDetail, fnFilter) {
      this.id = _iId++;
      oDetail.cache = !!oDetail.cache;
      this.detail = oDetail;
      this.filter = fnFilter;
    };
    return {
      /**
       * @example  
       *  var i=0;
       *  for(var j=0;j<=10;j++) 
       *    _ajaxManager.push({
       *      sUrl : '/behavior/rpc?doc&t='+Math.random(),
       *      onOk : function(o) {
       *        console.log(i++);
       *      }
       *    })
       * @param {object} oDetail detail ajax config
       * @config {string} sUrl the url to be requested
       * @config {object} oGet name value pair of get parameters
       * @config {object} sPost the post content, will auto be json encode
       * @config {integer} timeout timeout of the request, milliseconds
       * @config {boolean} cache cache or not, default false
       * @config {function} onOk on success handler
       * @config {function} onFail on failure handler
       * @config {function} fnFilter queueing request will be aborted when filter return false
       * @config {dom|id|selector} target response will be target's innerHTML
       */
      push : function(oDetail) {
        var fnFilter = oDetail.fnFilter;
        var oReq = new _Req(oDetail, fnFilter), _aTempQueue = [];
        if (fnFilter) {

          //filter the waiting queue
          _L.each(_aQueue, function(o, i) {
            if (fnFilter(o, oDetail)) {
              _aTempQueue.push(o);
            }
          });
          _aQueue = _aTempQueue;

          //filter the requesting queue
          _L.each(_aRequesting, function(o, i) {
            if (!fnFilter(o, oDetail)) {
              o[1].xhr.abort();
              _fnFinish(o[0]);
            }
          });
        }
        _aQueue.push(oReq);
        _sendRequest();
      }
    };
  })(),

  /**
   * a wrap of the ajaxManager.push method, provide easier parameter format
   * @param {string|object} sUrl the requesting url or the request object
   * @param {function|object} onOk the on success callback or the request object
   * @param {object} obj the request object, equals the only param of ajaxManager.push
   */
  _p = function(sUrl, onOk, obj) {
    obj = obj || {};
    if (typeof onOk== 'object') {
      obj = onOk;
    } else if (typeof onOk == _FUNCTION) {
      obj.onOk = onOk;
    }
    if (typeof sUrl == 'object') {
      obj =  sUrl;
    } else if (typeof sUrl == _STRING) {
      obj.sUrl = sUrl;
    }
    _ajaxManager.push(obj);
  };

  return {
    load : _SELF.load,
    ajax : _ajax,
    p : _p
  };

}()));

/**
 *
 * cookie.get : function(name)
 * cookie.del : function(name, path, domain)
 * cookie.set : function(name, value, config) expires, path, domain, secure
 * @module Cookie
 */
_SELF.add('Cookie', (function() {

  /**
   * Gets a cookie's value
   * @param {String} name The name of the cookie to get
   * @return {String} The cookie's value, or false if it can't find the cookie
   * @member cookies
   * @return String
   * @public
   */
  var get = function(name) {

    var cookie = _DOCUMENT.cookie,
      end,
      pre = name + '=',
      begin  = cookie[_INDEXOF]('; ' + pre),
      result;

    if (begin == -1) {
      begin = cookie[_INDEXOF](pre);
      if (begin !== 0) {
        return '';
      }
    } else {
      begin += 2;
    }

    end = cookie[_INDEXOF](';', begin);
    if (end == -1) {
      end = cookie[_LENGTH];
    }

    cookie = cookie.substring(begin + pre[_LENGTH], end);
    try {
      result = decodeURIComponent(cookie);
    } catch (e) {
      result = cookie;
    }
    return result;
  },

  /**
   * Removes a cookie that has already been set
   * @param {String} name The name of the cookie to remove
   * @param {String} path The path of the cookie to remove
   * @param {String} domain The domain of the cookie to remove
   * @member cookies
   * @public
   */
  del = function(name, path, domain) {

    var cookie = name + '=';

    if (!get(name)) {
      return;
    }
    if (path) {
      cookie += '; path=' + path;
    }
    if (domain) {
      cookie += '; domain=' + domain;
    }
    cookie += '; expires=Thu, 01-Jan-70 00:00:01 GMT';

    _DOCUMENT.cookie = cookie;

  },

  /**
   * Tells the browser to set a cookie
   * @param {String} name The name of the cookie to set
   * @param {String} value The value to set in the cookie
   * @param {Object} config extra parameters expires, path, domain, secure
   * @config {integer} expires The date that the cookie expires (value of Date.valueOf)
   * @config {String} path The path of the cookie to set
   * @config {String} domain The domain of the cookie to set
   * @config {Boolean} secure Whether to make the cookie secure or not
   * @member cookies
   * @public
   */
  set = function(name, value, config) {
    config = config || {};

    var cookie = name + '=' + encodeURIComponent(value), tmp;

    if (config.expires) {
      tmp = config.expires;
      if (!tmp.toGMTString) {
        tmp = new Date(tmp);
      }
      cookie += '; expires=' + tmp.toGMTString();
    }
    if (config.path) {
      cookie += '; path=' + config.path;
    }
    if (config.domain) {
      cookie += '; domain=' + config.domain;
    }
    if (config.secure) {
      cookie += '; secure';
    }

    _DOCUMENT.cookie = cookie;
  };

  return {
    get : get,
    del : del,
    set : set
  };
})());

/**
 * modified from : http://ejohn.org/blog/simple-javascript-inheritance/
 * @example
 *  var Person = __.Class.extend({
 *    init: function(isDancing){
 *      this.dancing = isDancing;
 *    }
 *  });
 *
 *  var Ninja = Person.extend({
 *    init: function(){
 *      this._super( false );

 *    }
 *  });
 *
 *  var p = new Person(true);
 *  p.dancing; // => true
 *
 *  var n = new Ninja();
 *  n.dancing; // => false 
 */
_SELF.add('Class', (function(){
  var initializing = _FALSE, fnTest = _getReg('xyz').test(function(){var xyz;}) ? _getReg('\\b_super\\b') : _getReg('.*');

  // The base Class implementation (does nothing)
  var Class = function(){};

  // Create a new Class that inherits from this class
  Class.extend = function(prop) {
    var _super = this[_PROTOTYPE];

    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = _TRUE;
    var prototype = new this();
    initializing = _FALSE;

    var repFn = function(name, fn) {
      return function() {
        var tmp = this._super;

        // Add a new ._super() method that is the same method
        // but on the super-class
        this._super = _super[name];

        // The method only need to be bound temporarily, so we
        // remove it when we're done executing
        var ret = fn[_APPLY](this, arguments);
        this._super = tmp;

        return ret;
      };
    };

    // Copy the properties over onto the new prototype
    for (var name in prop) {
      if (prop.hasOwnProperty(name)) {
        // Check if we're overwriting an existing function
        prototype[name] = typeof prop[name] == "function" &&
          typeof _super[name] == "function" && fnTest.test(prop[name]) ?
            repFn(name, prop[name]) : prop[name];
      }
    }

    // The dummy class constructor
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing && this.init ) {
        this.init[_APPLY](this, arguments);
      }
    }

    // Populate our constructed prototype object
    Class[_PROTOTYPE] = prototype;

    // Enforce the constructor to be what we expect
    Class.constructor = Class;

    // And make this class extendable
    Class.extend = arguments.callee;

    return Class;
  };
  return Class;
}()));

_SELF.add('Drag', (function(){//depends on Lang, Dom, Event
  var _D = _SELF.Dom, _E = _SELF.Event, _L = _SELF.Lang,
  opacity = 'opacity',
  __hlStack,
  __purge = function() {
    if (__hlStack) {
      _E.off(_DOCUMENT, 'mousemove', __hlStack[0]);
      _E.off(_DOCUMENT, 'mouseup', __hlStack[1]);
      __hlStack = _NULL;
    }
  },
  __getopacity = function(el) {
    var r;
    if (opacity in el.style) {
      r = el.style[opacity];
    } else {
      r = 100;
      try {
        r = el.filters['DXImageTransform.Microsoft.Alpha'][opacity];
      } catch(e) {
        try {
          r = el.filters('alpha')[opacity];
        } catch(err) {}
      }
      r = r / 100;
    }
    return r;
  },
  __setopacity = function(el, op) {
    if (opacity in el.style) {
      el.style.opacity = op;
    }

    if (_L.isString(el.style.filter)) {
      el.style.filter = 'alpha(opacity=' + (op * 100).toFixed(0) + ')';
      if (!el.currentStyle || !el.currentStyle.hasLayout) {
        el.style.zoom = 1;
      }
    }
  },

  /**
   * a simple drag&drop method
   * @param {object} el the element to be D&D
   * @param {boolean} withFade the fade effect onmousemove
   * @param {object} restrict an object with 4 attributes : {t,r,b,l}, means how much
   *                 pixels can be move towards that direction from current point
   * @param {function} fnUp function(left, top, a1.zone){}, on mouse up callback
   * @param {function} fnMove function(left, top, a1.zone){}, on mouse move callback
   * @param {object} cfg config object
   */
  _beginDrag = function (event, a1, withFade, restrict, fnUp, fnMove, cfg) {
    var as = a1.style, _on = _E.on;

    withFade = withFade === _FALSE ? _FALSE : _TRUE;
    cfg = cfg || {};

    __purge();

    a1.oriZ = _L.isEmpty(as.zIndex) ? 1 : as.zIndex;
    a1.style.zIndex = ++_SELF.topLayer;
    if (!a1.moved) {
      a1.moved = _TRUE;
      if (as.position.toLowerCase()[_INDEXOF]('absolute') == -1) {
        as.position = "relative";
      }

      if (as.left) {
        a1.oriL = parseInt(as.left, 10);
      } else {
        a1.oriL = 0;
        as.left = '0px';
      }
      if (as.top) {
        a1.oriT = parseInt(as.top, 10);
      } else {
        a1.oriT = 0;
        as.top = '0px';
      }
      if (restrict) {
        a1.zone = {
          top : a1.oriT - restrict.t,
          right : a1.oriL + restrict.r,
          bottom : a1.oriT + restrict.b,
          left : a1.oriL - restrict.l
        };
      }
    }
    var x = parseInt(as.left, 10),
      y = parseInt(as.top, 10),
      a2 = withFade ? __getopacity(a1) : 0,
      a3 = event.clientX - x,
      a4 = event.clientY - y,
      winXY = _D.getWindowXY(),
      hlOnUp = function () {
        __purge();
        if (withFade) {
          __setopacity(a1, a2);
        }
        //withFade && changeOpac(a2, a1);
        a1.style.zIndex = a1.oriZ;
        _SELF.topLayer--;
        __enableSelection(!!event.preventDefault);
        if (fnUp) {
          fnUp(parseInt(as.left, 10), parseInt(as.top, 10), a1.zone);
        }
      },
      hlOnMove = function (ev) {
        var left, top, sXY, tmp, tmp2;

        left = ev.clientX - a3;
        top = ev.clientY - a4;

        if (a1.zone) {
          var az = a1.zone;
          left = az.left > left ? az.left : left;
          left = az.right < left ? az.right : left;
          top = az.top > top ? az.top : top;
          top = az.bottom < top ? az.bottom : top;
        }

        //do the auto scroll
        if (cfg.autoScroll) {
          sXY = _D.getScrollXY();
          tmp = cfg.autoScroll;
          if ('height' in tmp) { // vertical scroll
            tmp2 = tmp.height;
            if (top-sXY[1]-winXY[1] + tmp2 > tmp.bottomBorder) {//the mover is at the bottom
              top += tmp2;
              sXY[1] += tmp2;
              _WINDOW.scrollTo(sXY[0], sXY[1]);
              a4 -= tmp2;
            } else if (top - sXY[1] < tmp.topBorder) {
              top -= tmp2;
              sXY[1] -= tmp2;
              _WINDOW.scrollTo(sXY[0], sXY[1]);
              a4 += tmp2;
            }
          }
          if ('width' in tmp) { // horizontal scroll
            tmp2 = tmp.width;
            if (left-sXY[0]-winXY[0] + tmp2 > tmp.rightBorder) {
              left += tmp2;
              sXY[0] += tmp2;
              _WINDOW.scrollTo(sXY[0], sXY[1]);
              a3 -= tmp2;
            } else if (top - sXY[1] < tmp.topBorder) {
              top -= tmp2;
              sXY[0] -= tmp2;
              _WINDOW.scrollTo(sXY[0], sXY[1]);
              a3 += tmp2;
            }
          }
        }

        as.left = left + "px";
        as.top = top + "px";
        if (fnMove) {
          fnMove(left, top, a1.zone);
        }

        //important!
        return _FALSE;
      },
      oldDocHl,
      __disableSelection = function (isFF) {
        if (!isFF) {
          oldDocHl = _DOCUMENT.onselectstart;
          _DOCUMENT.onselectstart = function() {return _FALSE;};
        } else {
          oldDocHl = _DOCUMENT.onmousedown;
          _DOCUMENT.onmousedown = function (event) {event.preventDefault ();};
        }
      },
      __enableSelection = function (isFF) {
        if (!isFF) {
          _DOCUMENT.onselectstart = oldDocHl;
        } else {
          _DOCUMENT.onmousedown = oldDocHl;
        }
      };
    __disableSelection(!!event.preventDefault);
    if (withFade) {
      __setopacity(a1, cfg.opacity || 0.7);
    }
    _on(_DOCUMENT, 'mousemove', hlOnMove);
    _on(_DOCUMENT, 'mouseup', hlOnUp);
    __hlStack = [hlOnMove, hlOnUp];
  };

  return {
    purge : __purge,
    bind : function(el, withFade, restrict, fnUp, fnMove, cfg) {
      el = _SELF.Dom.f(el);
      _SELF.Event.on(el, 'mousedown', function(e) {
        _beginDrag(e, el, withFade, restrict, fnUp, fnMove, cfg);
      });
    },
    unbind : function(el) {
    },
    handler : _beginDrag
  };

}()));

_objExtend(_SELF, {

  /**
   * create the object link, set value to the last property 
   * @param {string} varName a string of object chain name, such as "__.util.addEl"
   * @param {mixed} newValue the new value
   * @description if don't give the new value, the last properties' value will not change if it's not undefined, or it will be given a new value : an empty object
   */
  set : function(varName, newValue){
    var bAry = varName[_SPLIT]('.');
    var d = _WINDOW, key;
    for(var i=0,j=bAry[_LENGTH]-1;i<j;++i){
      d[bAry[i]] = d[bAry[i]] || {};
      d = d[bAry[i]];
    }
    key = bAry[bAry[_LENGTH]-1];
    d[key] = newValue === _UNDEFINED?
      (d[key] === _UNDEFINED? {} : d[key]) : newValue;
    return d[key];
  },

  /**
   * get value of the last property in the object link
   * @param {string} varName a string of object chain name, such as "__.util.addEl"
   */
  get : function(varName) {
    var bAry = varName[_SPLIT]('.');
    var d = _WINDOW;
    for(var i=0,j=bAry[_LENGTH];i<j;++i) {
      //d[bAry[i]] = d[bAry[i]] || {};
      if (!d) {
        return _UNDEFINED;
      }
      d = d[bAry[i]];
    }
    return d;
  },

  /**
   * whether the value of the last property is set in the object link
   * @param {string} varName a string of object chain name, such as "__.util.addEl"
   */
  isSet : function (varName) { //check if the the object link exist
    var d = _SELF.get(varName);
    if (d === _UNDEFINED) {
      return _FALSE;
    } else {
      return _TRUE;
    }
  },

  /**
   * call a function if some requirements are satisfied, usually used for lazy load and initiate
   * @param {function} fnCall the init function, 
   * @param {function|string} judge mixed, could be a function or a string(a set global var)
   * @param {integer} iInterval milliseconds of interval
   */
  toCall : function(fnCall, judge, iInterval) {
    iInterval = iInterval || 1000;
    var _fnJudge = typeof judge == _FUNCTION ? judge : function() {
      var _bRet = _FALSE;
      if(_SELF.isSet(judge)) {
        _bRet = _TRUE;
      }
      return _bRet;
    }, _sId;

    // do it now
    if (_fnJudge()) {
      fnCall();
    } else {
    //or later
      _sId = setInterval(function() {
        if(_fnJudge()) {
          clearInterval(_sId);
          fnCall();
        }
      }, iInterval);
    }
  },

  /**
   * begin debug, set debug config
   * @param {boolean} isDebug default to true
   */
  debug : function(isDebug) {
    _CFG[1] = isDebug === _FALSE ? _FALSE : _TRUE;
  },

  /**
   * unified layer count
   */
  topLayer : 1,

  //alias
  on : _SELF.Event.on,
  onReady : _SELF.Event.onReady,
  e : _each,
  log : _log

});

})(window);

/* class inherit sample (YAHOO edition, with enclosure, and private member, not prototype way)
var fn1 = function(p1) {
  //private member
  var in1;

  //initialize 
  (function(){
    in1 = p1 * 2;
  })();

  //public member
  var publicM = {
    getIn1 : function() {
      alert(in1);
    },
    setP1 : function(n) {
      in1 = n * 2;
    }
  };

  return publicM;
};
var fn2 = function(p1, p2) {
  //call the super constructor, could be called else where(eg: in the initializer)
  var superObj = new arguments.callee.superclass.constructor(p1);

  //private member
  var in2;

  //initialize 
  (function(){
    in2 = p2 * 3;
  })();

  //public member
  var publicM = {
    out1 : 'I am a public member',
    getIn2 : function() {
      alert(in2);
    },
    alertOut1 : function() {
      //use this to reference
      //can not reference to in2: (typeof this.in2 == undefined)
      alert(this.out1);
    }
  };

  __._.objExtend(publicM, superObj, false);
  
  return publicM;
};
__._.classExtend(fn2, fn1);//old
var obj_fn2 = new fn2(4,5);
obj_fn2.getIn1();
obj_fn2.getIn2();
obj_fn2.setP1(123);
obj_fn2.getIn1();
*/
///////////////////////////////////////////////////////////////////////////////////////
/*
    classExtend : function(subc, superc) {
      function buildSuperChain(superc , ary )  {
        ary.push(superc);
        if (typeof superc.__superclasses__ == "object") {
          for (var i=0; i < superc.__superclasses__.length; i++){
            buildSuperChain(superc.__superclasses__[i], ary);
          }
        }
      }
      if (typeof subc.prototype.constructor.__superclasses__ == "undefined") {
        subc.prototype.constructor.__superclasses__ = new Array();
      }
      buildSuperChain(superc, subc.prototype.constructor.__superclasses__);
      for (prop in superc.prototype) {
        if (typeof superc.prototype[prop] == "function") {
          subc.prototype[prop] = superc.prototype[prop];
        }
      }
    }
    ,*/
/* prototype edition example
function fn1(p1) {
  //constructor, all none-method properties
  this.in1 = p1;

  if (typeof fn1.prototype._stinited == 'undefined') {
    fn1.prototype.get1Sq = function() {
      alert(this.in1*this.in1);
    };

    fn1.prototype._stinited = true;
  }
}
function fn2(p1, p2) {
  fn1.call(this, p1);
  this.in2 = p2;

  if (typeof fn2.prototype._stinited == 'undefined') {
    __._.classExtend(fn2, fn1);

    fn2.prototype.get2Tri = function() {
      alert(this.in2*this.in2*this.in2);
    };

    fn2.prototype._stinited = true;
  }
}

var obj_fn2 = new fn2(4,5);
obj_fn2.get1Sq();
obj_fn2.get2Tri();

*/

/*
function click(id) {
  if( document.createEvent ) {
    var evObj = document.createEvent('MouseEvents');
    evObj.initEvent( 'click', true, false );
    document.getElementById(id).dispatchEvent(evObj);
  } else if( document.createEventObject ) {
    document.getElementById(id).fireEvent('onclick');
  }
}*/
//click('search-submit')

